package foliotracker;

import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Observable;
import java.util.UUID;

/**
 * Represents a collection of stocks, with methods to get the total price etc.
 */
public class Portfolio extends Observable implements Serializable
{
	private static final long serialVersionUID = 1L;
	private UUID id;
	private String name;
	private List<Stock> stocks;
	private List<PortfolioValue> foliohistory;
	private int rowChanged;
	
	public Portfolio(String name)
	{
		//this isn't called during deserialisation
		id = UUID.randomUUID();
		this.name = name;
		stocks = new ArrayList<Stock>();
		foliohistory = new ArrayList<PortfolioValue>();
	}
	
	/**
	 * Gets the portfolio's unique identifier.
	 * @return
	 */
	public UUID getID()
	{
		return id;
	}
	
	/**
	 * Gets the name of the portfolio.
	 * @return
	 */
	public String getName()
	{
		return name;
	}
	
	/**
	 * Sets the name of the portfolio and notifies observers of the change.
	 * @param value
	 */
	public void setName(String value)
	{
		name = value;
	}
	
	/**
	 * Gets the list of stocks in the portfolio.
	 */
	public List<Stock> getStocks()
	{
		return stocks;
	}
	
	
	/**
	 * Increases the amount of stock held with the specified stock symbol. If a stock object with the specified
	 * symbol is already present in the collection, the buy method of that object is called.  Otherwise,
	 * a new stock object is created and added to the collection, then the buy method called.
	 *  
	 * @param symbol The stock symbol, e.g. MSFT.
	 * @param name The name of the stock, e.g. Microsoft.
	 * @param date The date that the transaction took place.
	 * @param quantity The quantity of stock purchased.
	 * @param cost The price at which the stock was purchased.
	 * @param price The current price of the stock.
	 */
	public void buyStock(String symbol, String name, Date date, int quantity, double cost, double price)
	{
		Stock stock = getStockBySymbol(symbol);
		
		if (stock == null)
		{
			Stock newStock = new Stock(symbol, name, price);
			newStock.buy(date, quantity, cost);
			stocks.add(newStock);
			
			rowChanged = stocks.size() - 1;
			super.setChanged();
			super.notifyObservers();
		}
		else
		{	
			stock.buy(date, quantity, cost);
			stock.setPrice(price);
		}
		
		this.addCurrentValueToHistory();
	}
	
	/**
	 * Decreases the amount of stock held with the specified stock symbol.
	 * 
	 * @param symbol The symbol of the stock to sell, e.g. MSFT.
	 * @param quantity The quantity of stock to sell.
	 * @exception IllegalArgumentException Stock with the specified symbol cannot be found in the collection.
	 * @exception IllegalArgumentException quantity is greater than the total amount of stock held for the given symbol.
	 */
	public void sellStock(String symbol, int quantity)
	{
		Stock stock = getStockBySymbol(symbol);
		
		if (stock == null)
			throw new IllegalArgumentException(String.format("The symbol %s could not be found.", symbol));
		
		if (stock.getQuantity() < quantity)
			throw new IllegalArgumentException("quantity is greater than the total amount of stock held for the given symbol.");
		
		stock.sell(quantity);
		
		if (stock.getQuantity() == 0)
		{
			rowChanged = stocks.indexOf(stock);
			stocks.remove(stock);
			
			super.setChanged();
			super.notifyObservers();
		}
		
		this.addCurrentValueToHistory();
	}
	
	/**
	 * Gets a list containing historical values for the portfolio.
	 * @return
	 */
	public List<PortfolioValue> getValueHistory()
	{
		return foliohistory;
	}
	
	/**
	 * If there is an item in the history for today, updates it to the current value; otherwise, adds a new value for today's date.
	 */
	public void addCurrentValueToHistory()
	{
		Date date = new Date();
		//create a new instance of PortfolioValue and add it to the list of other values
		PortfolioValue value = new PortfolioValue(date, this.getTotalValue(), this.getNetProfit());
		foliohistory.add(value);
	}
	
	/**
	 * Gets the total value of all stocks held in the portfolio.
	 * @return
	 */
	public double getTotalValue()
	{
		double sum = 0;
		for (Stock stock : stocks){
			sum += stock.getTotalValue();
		}
		return sum;
	}
	
	/**
	 * Gets the total cost of stocks in the portfolio.
	 * @return
	 */
	public double getTotalInitialCost()
	{
		double sum = 0;
		for(Stock stock : stocks){
			sum += stock.getInitialCost();
		}
		return sum;
	}
	
	/**
	 * Gets the total net profit/loss for the portfolio.
	 * @return
	 */
	public double getNetProfit()
	{
		double sum = 0;
		for(Stock stock : stocks){
			sum += stock.getProfit();
		}
		return sum;
	}
	
	/**
	 * Gets the stock with the specified symbol.
	 * @param symbol
	 * @return The stock instance if one was found; otherwise, null.
	 */
	public Stock getStockBySymbol(String symbol)
	{
		for (Stock stock: stocks)
		{
			if (stock.getSymbol().equals(symbol))
			{
				return stock;
			}
		}
		
		return null;
	}
	
	/**
	 * Gets the percentage the stock value has grown by.
	 * @return
	 */
	public double getPercentageGain()
	{
		double cost = getTotalInitialCost();
		
		if (cost == 0)
		{
			return 0;
		}
		else
		{
			return (getTotalValue() - cost) / cost;
		}
	}
	
	/**
	 * Gets the index of the last stock added or removed.
	 * @return
	 */
	public int getLastChangedStockIndex()
	{
		return rowChanged;
	}
}
